查看原文
其他

Java集合面试题

我是程序汪 我是程序汪 2021-09-09

业务代码中集合使用的频率非常之高

我公司面试5K到25K的都会问,25K以上都是手写集合啦


版本:JDK1.8


集合大家庭


见集合面试题,我简单的总结下

1、List Set Map 的区别

2、ArrayList 和LinkedList 区别

4、HashMap 和 HashTable的区别

5、HashMap 和 ConcurrentHashMap 区别(数据结构、基本思想)

6、HashSet 和 HashMap 区别

7、HashSet 检查重复

8、hashCode() 和 equals()

9、集合的快速失败机制fail-fast

10、集合排序Comparable、Comparator

12、Collections工具、Collection接口(容易混淆)



1、List Set Map 的区别

  • List: 有序,可以多个元素引用相同的对象

  • Set: 无序,不重复,不可以多个元素引用相同对象

  • Map: 使用键值对存储,两个key可以引用相同的对象,但是key不能重复

我想说:HashSet的底层其实就是HashMap


jdk8的HashSet部分源码

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing
<tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}


2、ArrayList 和LinkedList 区别

ArrayList 和LinkedList 区别

  • ArrayList: 底层使用数组,存、读效率高;插入、删除特定位置效率低

  • LinkedList: 使用双向链表,插入、删除效率高

我想说:业务代码中你会发现到处都是ArrayList,极少看到有人用LinkedList,实际业务代码测试ArrayList插入删除其实也并不慢



3、Java集合的快速失败机制 “fail-fast”

代码经验不丰富的朋友,写业务代码时最容易犯的错误


记住2点

1.利用Iterator操作集合更新操作,remove、add2.一个异常 ConcurrentModificationException


阿里代码规范也是这么规定的

正例
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}


4、为什么HashMap中String适合作为K

回答:内部已重写了equals()hashCode()等方法,遵守了HashMap内部的规范,不容易出现Hash值计算错误的情况;

所以如果你要你的POJO类当HashMap的K,必须实现equals()、hashCode()方法


我无聊的点开放jdk源码,看了下包装类 Boolean 、Long、Byte、Double 都实现了 Comparable接口


jdk8中 源码都很费脑子,我就简单看下关键部分,面试问到有东西回复即可


String的equals()源码,有些面试官还真喜欢问

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}



5、集合排序Collections.sort的两种用法

接口

Comparable 有比较能力的

Comparator 比较器

Collections是工具类 Collection是接口,不要搞混淆了哦


注意String是实现了Camparable接口的,自带比较能力的

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence



业务代码一般都是通过Comparator内部类来实现排序

比较简单、方便

Collections.sort(intList,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// 返回值为int类型,大于0表示正序,小于0表示逆序
return o2-o1;
}
});

Comparable 需要修改pojo类比较麻烦


下面是jdk8,流式语法的排序,简洁到极致

List<Apple> inventory = Lists.newArrayList(
new Apple(10, "red",DemoDate.string2Date("1990-12-31 23:59:00", DemoDate.DateFormatEnum.SIMPLE_FORMAT.getCode())),
new Apple(5, "red",DemoDate.string2Date("1990-12-31 23:59:00", DemoDate.DateFormatEnum.SIMPLE_FORMAT.getCode())),
new Apple(1, "green",DemoDate.string2Date("1991-12-31 23:59:00", DemoDate.DateFormatEnum.SIMPLE_FORMAT.getCode())),
new Apple(15, "green",DemoDate.string2Date("1992-12-31 23:59:00", DemoDate.DateFormatEnum.SIMPLE_FORMAT.getCode())),
new Apple(2, "red",DemoDate.string2Date("1980-12-31 23:59:00", DemoDate.DateFormatEnum.SIMPLE_FORMAT.getCode())));
System.out.println("=========华丽分割线====默认是升序=================");
//默认是升序
Collections.sort(inventory, Comparator.comparing(Apple::getBirthday));
inventory.forEach(System.out::println);
System.out.println("=========华丽分割线====默认是降序=================");
//默认是升序
Collections.sort(inventory, Comparator.comparing(Apple::getBirthday).reversed());
inventory.forEach(System.out::println);
System.out.println("=========华丽分割线=====================");
System.out.println("=========华丽分割线=====================");
//先根据生日排序(倒叙),然后相同生日就根据重量排序
Collections.sort(inventory, Comparator.comparing(Apple::getBirthday)
.reversed()
.thenComparing(Apple::getWeight));




5、面试有时候会问到K/V可以为null(多么老残的问题)

特别注意下HashMap的K/V允许null


6、HashMap的具体实现知道吗?


能把数据结构、主要的特性说出来面试也加分的。

源码我也记不住,真的太复杂了

注意把jdk版本说清楚 1.7和1.8是不一样的哦

说清楚1.8也可以




HashMap 里面是一个数组,然后数组中每个元素是一个单向链表


上图中,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。


capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍

loadFactor:负载因子,默认为 0.75

threshold:扩容的阈值,等于 capacity * loadFactor



HashMap是基于 哈希表 的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null 值 和 null 键。HashMap 存储的是键值对,HashMap很快,但是不保证映射的顺序,特别是它不保证改顺序不变;

HashMap 实际上是一个“链表散列”的数据结构,即 数组 和 链表 的结合体。

  • 数组: 存储区间连续,占用内存严重,寻址容易,插入删除困难;

  • 链表:存储区间离散,占用内存比较宽松,寻址困难,插入删除容易;


基本原理:

  1. 声明一个下标范围比较大的数组来存储元素;

  2. 设计一个哈希函数来获取每一个元素的Key(关键字)的函数值(数组下标, hash值)相对应

  3. 数组存储的元素的一个Entry类,这个类有三个数据域,key、value、next(指向下一个Entry)




Java8 HashMap


transient Node<K,V>[] table; //数组+链表


transient int modCount; //快速失败机制,防并发


数组+链表+红黑色

翻译成代码: table + Node + TreeNode


Java8 中使用 Node(链表),核心属性 key,value,hash 和 next ,Node 只能用于链表的情况,

红黑树的情况需要使用 TreeNode。

我们根据数组元素中,第一个节点数据类型是 Node 还是 TreeNode 来判断该位置下是链表还是红黑树的。



: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存